/*
 * edulisp.cpp
 *
 *  Created on: 07.04.2011
 *      Author: Дворников А.А.
 *              Миночкина Н.О.
 *              Моисеев А.И.
 *
 *      Revision: 292
 */

#include <iostream>
#include <stdlib.h>
#include <string>
#include <iomanip>
#include <fstream>
//Библиотечные инклады

#include "edulisp.hpp"
#include "edulisp_scanner.hpp"
#include "edulisp_language.hpp"
#include "edulisp_nametable.hpp"
#include "edulisp_analyser.hpp"
#include "edulisp_interpreter.hpp"
//Локальные инклады

using namespace std;
//Определения используемых пространств имён

int main(int argc, char* argv[])
{
	NameTable NameTable_;
	int ErrorCode;
	FunctionDescriptor RootFunction;


	{
		/*string s_test =
				"(defun OUTPUT (from to)"
				"	(print from \" -> \" to \"\\n\"))"
				"(defun TRANSFER (from to aux N)"
				"	(if (= N 1)"
				"		(OUTPUT from to)"
				"		(progn "
				"			(TRANSFER from aux to (- N 1))"
				"			(OUTPUT from to)"
				"			(TRANSFER aux to from (- N 1)))))"
				"(defun HANOI-TOWERS (height)"
				"	(TRANSFER \"A\" \"B\" \"C\" height))"
				"(HANOI-TOWERS (promt \"Enter height\"))"*/

		; // end of towers

		// Если менее одного параметра
		if (argc < 2)
			// Ошибка
			return EXIT_FAILURE_ARGUMENTS;

		//Читаем текст программы из файла
		string s_test;
		ifstream source(argv[1], ifstream::in);
		while(source.good())
			s_test += source.get();
		source.close();

		//clog << s_test.length();

		cout << "Scanning attempt: \"" << s_test << "\"..." << endl;

		//cin >> s_test;
		//Проводим лексический анализ и формируем промежуточную форму
		ErrorCode = Scan(s_test, RootFunction.GetLexemeList(), NameTable_);
		if (ErrorCode > SEC_OK)
		{
			cout << ((ErrorCode == SEC_ERROR) ? "Syntax error during scan phase."
					: "Internal scanner error.") << endl;
			return EXIT_FAILURE_SCANNER;
		}

		//Выводим поток лексем
		cout << "Lexeme stream:" << endl;
		int LexemeCounter = 0;
		for (LexemeList::iterator i = RootFunction.GetLexemeList().begin();
				i != RootFunction.GetLexemeList().end(); i++, LexemeCounter++)
		{
			cout << *i << ' ';
			if (LexemeCounter == 40)
			{
				cout << endl;
				LexemeCounter = 0;
			}
		}
		cout << endl;
		cout << endl;
		//Выводим таблицу имен
		cout << "Name table size: " << NameTable_.size() << '.' << endl;
		cout << "Lexeme list size: " << RootFunction.GetLexemeList().size() << '.' << endl << endl;

		cout << "Name table:" << endl;
		cout << left << '|' << setw(6) << "ID" << '|' << setw(15) << "TEXT"
				<< '|' << setw(6) << "OWNER" << '|'
				<< setw(20) << "VALUE" << '|' << setw(10) << "TYPE" << '|'
				<< setw(15) << "LEXEME TYPE" << '|' << endl;
		int i = 1;
		for (NameTable::iterator i2 = NameTable_.begin(); i2 != NameTable_.end();
				i2++, i++)
		{
			cout << '|' << setw(6) << i - 1 << '|' << setw(15)
					<< ((*i2)->GetText() ? *((*i2)->GetText()) : "NULL")
					<< '|' << setw(6) << (*i2)->GetOwner()
					<< '|' << setw(20);
			switch((*i2)->GetDataType())
			{
			case DT_STRING:
				cout << '\"' + *((*i2)->GetString()) + '\"' << '|' << setw(10)
					<< "STRING";
				break;
			case DT_INT:
				cout << *((*i2)->GetInteger()) << '|' << setw(10) << "INTEGER";
				break;
			case DT_DOUBLE:
				cout << *((*i2)->GetFloat()) << '|' << setw(10) << "FLOAT";
				break;
			case DT_NULL:
				cout << "NULL" << '|' << setw(10) << "NULL";
				break;
			case DT_USERFUNCTION:
				cout << ((*i2)->ZeroValue() ? "NULL" : "") << '|' << setw(10)
					<< "USERFUNCTION";
			}
			cout << '|' << setw(15);
			switch((*i2)->GetLexemeType())
			{
			case LT_CONST:
				cout << "CONST";
				break;
			case LT_ID:
				cout << "ID";
				break;
			default:
				cout << "UNKNOWN";
			}
			cout << "";
			cout << '|' << endl;
		}

		//Провдим синтаксический анализ
		ErrorCode = Analyse(RootFunction.GetLexemeList(), NameTable_);
		if (ErrorCode)
		{
			cout << "Error during analyse phase. Code E" << ErrorCode << ".\n";
			//return EXIT_FAILURE_ANALYSER;
		} else cout << "No analyser errors for you. Sorry." << endl;

		Interpreter Int_Test(std::cin, std::cout);
		NameTable_Entry *Result;
		//Исполняем промежуточную форму программы
		ErrorCode = Int_Test.Execute(NameTable_, RootFunction, NULL, Result);
		if (ErrorCode)
		{
			cout << "Interpreter error. Code E" << ErrorCode << '.' << endl;
			return EXIT_FAILURE_INTERPRETER;
		}

		if (Result)
			cout << "Result: " << *Result << endl;
	}

	return EXIT_SUCCESS;
}
